home *** CD-ROM | disk | FTP | other *** search
- #include <string.h>
- #include <stdlib.h>
- #include <osbind.h>
- #include "global.h"
-
- #define POOLSIZE_DEFAULT 50000L
- #define POOLSIZE_VAR "ALLOCMEM"
- #define MIN_CHUNK (sizeof(chunk_header) + 8)
- #define ALLOCED 0xAB000000UL
- #define FREED 0xFB000000UL
-
- #define round(n) (((n) + 7) & ~7)
- #define is_alloced(H) (((H)->size & 0xFF000000L) == ALLOCED)
- #define is_freed(H) (((H)->size & 0xFF000000L) == FREED)
- #define chunksize(H) ((H)->size & 0x00FFFFFFL)
-
- typedef struct chunk_header {
- struct chunk_header *next;
- unsigned long size;
- } chunk_header;
-
- unsigned char *pool = 0;
- unsigned long poolsize = POOLSIZE_DEFAULT;
- static chunk_header *arena;
-
- int init_mem(void)
- {
- register char *s = do_getvstr(POOLSIZE_VAR);
-
- if (s)
- poolsize = strtoul(s, NULL, 0);
- if (poolsize <= MIN_CHUNK) {
- Cconws("alloc pool size too small - using default size\r\n");
- poolsize = POOLSIZE_DEFAULT;
- }
- pool = (unsigned char *)Mxalloc(poolsize, 0x2B); /* global, prefer TT RAM */
- if (!pool) {
- Cconws("Unable to allocate alloc pool\r\n");
- return 0;
- }
- arena = (chunk_header *)pool;
- arena->next = 0;
- arena->size = (poolsize - sizeof(chunk_header)) | FREED;
- return 1;
- }
-
- /* join_next() -- Joins chunk |H| with the next block and sets its state to
- |state|. WARNING: Assumes |H->next| is not NULL. */
- static void join_next(register chunk_header *H, unsigned long state)
- {
- register chunk_header *H2 = H->next;
- H->next = H2->next;
- H->size = (chunksize(H) + chunksize(H2) + sizeof(chunk_header)) | state;
- }
-
- /* try_split() -- split chunk |block| into two chunks, the first of size
- |newsize|, if there's room for a second chunk. The new chunk (if
- any) is marked FREED; the (possibly smaller) original block is marked
- ALLOCED. WARNING: assumes |newsize| is already round()'ed. */
- static void try_split(chunk_header *block, unsigned long newsize)
- {
- register chunk_header *H;
-
- if (chunksize(block) - newsize >= MIN_CHUNK) {
- H = (chunk_header *)((unsigned char *)(block + 1) + newsize);
- H->next = block->next;
- H->size = (chunksize(block) - newsize - sizeof(chunk_header)) | FREED;
- block->next = H;
- block->size = newsize | ALLOCED;
-
- /* if the next block is free, merge the new block into it */
- if (H->next && is_freed(H->next))
- join_next(H, FREED);
- } else {
- block->size = chunksize(block) | ALLOCED;
- }
- }
-
- char* do_KRmalloc(int32 size)
- {
- register chunk_header *H;
-
- size = round(size);
- if (size >= 0x01000000L || size == 0) {
- #ifdef DEBUG
- debug("sls", "KRmalloc(", size, ") returns 0");
- #endif
- return 0;
- }
-
- for (H = arena; H; H = H->next) {
- if (!is_freed(H) || chunksize(H) < size)
- continue;
- try_split(H, size);
- #ifdef DEBUG
- debug("slsp", "KRmalloc(", size, ") returns ", (void *)(H+1));
- #endif
- return (char *)(H + 1);
- }
- #ifdef DEBUG
- debug("sls", "KRmalloc(", size, ") returns 0");
- #endif
- return 0;
- }
-
- void do_KRfree(void *mem)
- {
- register chunk_header *H = (chunk_header *)mem;
-
- #ifdef DEBUG
- debug("sps", "in KRfree(", mem, ")");
- #endif
- if (!mem)
- return;
- H--; /* step back to the header */
- if (!is_alloced(H)) {
- #ifdef DEBUG
- debug("s", "\tblock not alloced?");
- #endif
- return;
- }
-
- H->size = chunksize(H) | FREED;
-
- if (H->next && is_freed(H->next)) {
- /* next block is free; merge with it */
- join_next(H, FREED);
- }
-
- if (H != arena) {
- /* there is a previous chunk; merge with it if it's freed */
- register chunk_header *H2;
-
- for (H2 = arena; H2 && H2->next != H; H2 = H2->next)
- continue;
- if (!H2) { /* this shouldn't happen */
- #ifdef DEBUG
- debug("s", "\tno previous block?");
- #endif
- return;
- }
- if (is_freed(H2)) {
- join_next(H2, FREED);
- H = H2;
- }
- }
- #ifdef DEBUG
- debug("s", "\tdone");
- #endif
- }
-
- int32 do_KRgetfree(int16 flag)
- {
- register chunk_header *H;
- register unsigned long size = 0;
-
- #ifdef DEBUG
- debug("s", "in KRgetfree()");
- #endif
- if (flag) {
- for (H = arena; H; H = H->next) {
- if (is_freed(H) && chunksize(H) > size)
- size = chunksize(H);
- }
- } else {
- for (H = arena; H; H = H->next) {
- if (is_freed(H))
- size += chunksize(H);
- }
- }
- return size;
- }
-
- char* do_KRrealloc(char *mem, int32 newsize)
- {
- register chunk_header *H = (chunk_header *)mem;
-
- #ifdef DEBUG
- debug("s", "in KRrealloc()");
- #endif
- if (!mem) {
- mem = do_KRmalloc(newsize);
- if (mem)
- memset(mem, 0, newsize);
- return mem;
- }
-
- if (newsize == 0) {
- do_KRfree(mem);
- return 0;
- }
-
- H--; /* step back to the header */
- if (!is_alloced(H))
- return 0;
-
- newsize = round(newsize);
- if (newsize <= chunksize(H)) {
- /* if we're downsizing, we may have room to split the chunk */
- try_split(H, newsize);
- return mem;
- } else if (H->next && is_freed(H->next) &&
- (chunksize(H) + chunksize(H->next) +
- sizeof(chunk_header)) >= newsize) {
- /* Next chunk is free and big enough to accommodate the new size;
- join it with the current chunk */
- join_next(H, ALLOCED);
- /* We may even have room to split off part of the newly joined chunk */
- try_split(H, newsize);
- return mem;
- } else {
- register char *newmem = do_KRmalloc(newsize);
-
- if (!newmem)
- return 0;
- memcpy(newmem, mem, chunksize(H));
- do_KRfree(mem);
- return newmem;
- }
- }
-